/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ulduar.h"
#include "Vehicle.h"
#include "Player.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Transport.h"

class instance_ulduar : public InstanceMapScript
{
public:
    instance_ulduar() : InstanceMapScript("instance_ulduar", 603) { }

    InstanceScript* GetInstanceScript(InstanceMap* pMap) const
    {
        return new instance_ulduar_InstanceMapScript(pMap);
    }

    struct instance_ulduar_InstanceMapScript : public InstanceScript
    {
        instance_ulduar_InstanceMapScript(Map* pMap) : InstanceScript(pMap) {Initialize();};

		uint32 m_auiEncounter[MAX_ENCOUNTER];
		uint32 C_of_Ulduar_MASK;

		// Bosses
		uint64 m_uiLeviathanGUID;
		uint64 m_uiIgnisGUID;
		uint64 m_uiRazorscaleGUID;
		uint64 m_uiXT002GUID;
		uint64 m_auiAssemblyGUIDs[3];
		uint64 m_uiKologarnGUID;
		uint64 m_uiAuriayaGUID;
		uint64 m_uiMimironGUID;
		uint64 m_uiHodirGUID;
		uint64 m_uiThorimGUID;
		uint64 m_uiFreyaGUID;
		uint64 m_uiVezaxGUID;
		uint64 m_uiYoggSaronGUID;
		uint64 m_uiAlgalonGUID;

		// Flame Leviathan
		uint64 m_leviathanDoorsGUID;
		uint64 m_leviathanVisualTowers[4][2];
		uint64 m_RepairSGUID[2];
		uint64 m_lightningWalls[2];
		bool m_leviathanTowers[4];
		std::list<uint64> _leviathanVehicles;
		uint32 m_unbrokenAchievement;

		// Razorscale
		uint64 m_RazorscaleHarpoonFireStateGUID[4];

		// XT-002
		uint64 m_xt002DoorsGUID;
		uint64 m_xt002Doors2GUID;

		// Assembly of Iron
		uint64 m_assemblyDoorsGUID;
		uint64 m_archivumDoorsGUID;

		// Thorim
		uint64 m_thorimGameobjectsGUID[5];

		// Mimiron
		uint64 m_MimironDoor[3];
		uint64 m_MimironLeviathanMKIIguid;
		uint64 m_MimironVX001guid;
		uint64 m_MimironACUguid;

		// Freya
		uint64 m_FreyaElder[3];
		uint32 m_conspeedatoryAttempt;

		// Yogg-Saron
		uint64 m_saraGUID;
		uint64 m_yoggsaronBrainGUID;
		uint64 m_yoggsaronDoorsGUID;

		// Algalon
		uint64 m_algalonSigilDoorGUID[3];
        uint64 m_algalonFloorGUID[2];
        uint64 m_algalonUniverseGUID;
        uint64 m_algalonTrapdoorGUID;
        uint64 m_brannBronzebeardAlgGUID;
        uint32 m_algalonTimer;
		
		// Shared
		EventMap _events;
		bool m_mimironTramUsed;
		uint64 m_mimironTramGUID;
		uint64 m_keepersgateGUID;
		uint64 m_keepersGossipGUID[4];
		
		
		void Initialize()
		{
			// Bosses
			memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
			C_of_Ulduar_MASK = 0;
			memset(&m_auiAssemblyGUIDs, 0, sizeof(m_auiAssemblyGUIDs));
			m_uiLeviathanGUID       = 0;
			m_uiIgnisGUID           = 0;
			m_uiRazorscaleGUID      = 0;
			m_uiXT002GUID           = 0;
			m_uiKologarnGUID        = 0;
			m_uiAuriayaGUID         = 0;
			m_uiMimironGUID         = 0;
			m_uiHodirGUID           = 0;
			m_uiThorimGUID          = 0;
			m_uiFreyaGUID           = 0;
			m_uiVezaxGUID           = 0;
			m_uiYoggSaronGUID       = 0;
			m_uiAlgalonGUID         = 0;

			// Flame Leviathan
			memset(&m_leviathanTowers, 1, sizeof(m_leviathanTowers));
			memset(&m_leviathanVisualTowers, 0, sizeof(m_leviathanVisualTowers));
			memset(&m_RepairSGUID, 0, sizeof(m_RepairSGUID));
			memset(&m_lightningWalls, 0, sizeof(m_lightningWalls));
			m_leviathanDoorsGUID	= 0;
			_leviathanVehicles.clear();
			m_unbrokenAchievement	= 1;

			// Razorscale
			memset(&m_RazorscaleHarpoonFireStateGUID, 0, sizeof(m_RazorscaleHarpoonFireStateGUID));

			// XT-002
			m_xt002DoorsGUID		= 0;
			m_xt002Doors2GUID       = 0;

			// Assembly of Iron
			m_assemblyDoorsGUID		= 0;
			m_archivumDoorsGUID		= 0;

			// Thorim
			memset(&m_thorimGameobjectsGUID, 0, sizeof(m_thorimGameobjectsGUID));

			// Mimiron
			memset(&m_MimironDoor, 0, sizeof(m_MimironDoor));
			m_MimironLeviathanMKIIguid = 0;
			m_MimironVX001guid = 0;
			m_MimironACUguid = 0;

			// Freya
			memset(&m_FreyaElder, 0, sizeof(m_FreyaElder));
			m_conspeedatoryAttempt	= 0;

			// Yogg-Saron
			m_saraGUID				= 0;
			m_yoggsaronBrainGUID	= 0;
			m_yoggsaronDoorsGUID	= 0;

			// Algalon
			memset(&m_algalonSigilDoorGUID, 0, sizeof(m_algalonSigilDoorGUID));
			memset(&m_algalonFloorGUID, 0, sizeof(m_algalonFloorGUID));
			m_algalonUniverseGUID	= 0;
			m_algalonTrapdoorGUID	= 0;
			m_brannBronzebeardAlgGUID	= 0;
			m_algalonTimer			= 0;
			
			// Shared
			_events.Reset();
			memset(&m_keepersGossipGUID, 0, sizeof(m_keepersGossipGUID));
			m_mimironTramUsed		= false;
			m_mimironTramGUID		= 0;
			m_keepersgateGUID		= 0;
		}

		void FillInitialWorldStates(WorldPacket& packet)
		{
			packet << uint32(WORLD_STATE_ALGALON_TIMER_ENABLED) << uint32(m_algalonTimer && m_algalonTimer <= 60);
			packet << uint32(WORLD_STATE_ALGALON_DESPAWN_TIMER) << uint32(std::min<uint32>(m_algalonTimer, 60));
		}

		void OnPlayerEnter(Player* player)
		{
			// mimiron tram:
			instance->LoadGrid(2307.0f, 284.632f);
			if (GameObject* MimironTram = instance->GetGameObject(m_mimironTramGUID))
				player->UpdateVisibilityOf(MimironTram);

			if (!m_uiAlgalonGUID && m_algalonTimer && (m_algalonTimer <= 60 || m_algalonTimer == TIMER_ALGALON_TO_SUMMON))
			{
				TempSummon* algalon = instance->SummonCreature(NPC_ALGALON, AlgalonLandPos);
				if (!algalon)
					return;

                if (m_algalonTimer <= 60)
				{
					_events.RescheduleEvent(EVENT_UPDATE_ALGALON_TIMER, 60000);
                    algalon->AI()->DoAction(ACTION_INIT_ALGALON);
				}
                else // if (m_algalonTimer = TIMER_ALGALON_TO_SUMMON)
				{
					m_algalonTimer = TIMER_ALGALON_SUMMONED;
                    algalon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
				}
            }
        }

		bool IsEncounterInProgress() const
		{
			for (uint8 i = 0; i < (MAX_ENCOUNTER-1); ++i)
			{
				if (m_auiEncounter[i] == IN_PROGRESS)
					return true;
			}

			// Leviathan does not use IN_PROGRESS type, instead SPECIAL is set and never reset,
			// Check if he is in combat.
			if (Unit* l = instance->GetCreature(m_uiLeviathanGUID))
				if (l->IsInCombat())
					return true;

			return false;
		}

		void ProcessEvent(WorldObject* obj, uint32 eventId)
		{
			// destory towers
			if (eventId >= EVENT_TOWER_OF_LIFE_DESTROYED && eventId <= EVENT_TOWER_OF_FLAMES_DESTROYED)
				SetData(eventId, 0);
		}

		void OnCreatureCreate(Creature* creature)
		{
			switch(creature->GetEntry())
			{
				case NPC_LEVIATHAN:
					m_uiLeviathanGUID = creature->GetGUID();
					break;
				case NPC_IGNIS:
					m_uiIgnisGUID = creature->GetGUID();
					break;
				case NPC_RAZORSCALE:
					m_uiRazorscaleGUID = creature->GetGUID();
					break;
				case NPC_XT002:
					m_uiXT002GUID = creature->GetGUID();
					break;
				case NPC_STEELBREAKER:
					m_auiAssemblyGUIDs[0] = creature->GetGUID();
					break;
				case NPC_MOLGEIM:
					m_auiAssemblyGUIDs[1] = creature->GetGUID();
					break;
				case NPC_BRUNDIR:
					m_auiAssemblyGUIDs[2] = creature->GetGUID();
					break;
				case NPC_KOLOGARN:
					m_uiKologarnGUID = creature->GetGUID();
					if (GetData(TYPE_KOLOGARN) == DONE)
					{
						creature->SetDisableGravity(true);
						creature->SetPosition(creature->GetHomePosition());
						creature->setDeathState(JUST_DIED);
						creature->StopMovingOnCurrentPos();
					}
					break;
				case NPC_AURIAYA:
					m_uiAuriayaGUID = creature->GetGUID();
					break;
				case NPC_MIMIRON:
					m_uiMimironGUID = creature->GetGUID();
					break;
				case NPC_HODIR:
					m_uiHodirGUID = creature->GetGUID();
					break;
				case NPC_THORIM:
					m_uiThorimGUID = creature->GetGUID();
					break;
				case NPC_FREYA:
					m_uiFreyaGUID = creature->GetGUID();
					break;
				case NPC_VEZAX:
					m_uiVezaxGUID = creature->GetGUID();
					break;
				case NPC_YOGGSARON:
					m_uiYoggSaronGUID = creature->GetGUID();
					break;
				case NPC_ALGALON:
					m_uiAlgalonGUID = creature->GetGUID();
					break;
				case NPC_HARPOON_FIRE_STATE:
					{
						if( creature->GetPositionX() > 595 )
							m_RazorscaleHarpoonFireStateGUID[3] = creature->GetGUID();
						else if( creature->GetPositionX() > 585 )
							m_RazorscaleHarpoonFireStateGUID[2] = creature->GetGUID();
						else if( creature->GetPositionX() > 575 )
							m_RazorscaleHarpoonFireStateGUID[1] = creature->GetGUID();
						else
							m_RazorscaleHarpoonFireStateGUID[0] = creature->GetGUID();
					}
					break;
				case NPC_MIMIRON_LEVIATHAN_MKII:
					m_MimironLeviathanMKIIguid = creature->GetGUID();
					break;
				case NPC_MIMIRON_VX001:
					m_MimironVX001guid = creature->GetGUID();
					break;
				case NPC_MIMIRON_ACU:
					m_MimironACUguid = creature->GetGUID();
					break;
				case NPC_FREYA_GOSSIP:
					m_keepersGossipGUID[TYPE_FREYA-TYPE_FREYA] = creature->GetGUID();
					ShowKeeperGossip(TYPE_FREYA, creature);
					break;
				case NPC_HODIR_GOSSIP:
					m_keepersGossipGUID[TYPE_HODIR-TYPE_FREYA] = creature->GetGUID();
					ShowKeeperGossip(TYPE_HODIR, creature);
					break;
				case NPC_THORIM_GOSSIP:
					m_keepersGossipGUID[TYPE_THORIM-TYPE_FREYA] = creature->GetGUID();
					ShowKeeperGossip(TYPE_THORIM, creature);
					break;
				case NPC_MIMIRON_GOSSIP:
					m_keepersGossipGUID[TYPE_MIMIRON-TYPE_FREYA] = creature->GetGUID();
					ShowKeeperGossip(TYPE_MIMIRON, creature);
					break;
				case NPC_ELDER_IRONBRANCH:
				case NPC_ELDER_STONEBARK:
				case NPC_ELDER_BRIGHTLEAF:
					m_FreyaElder[creature->GetEntry()-NPC_ELDER_IRONBRANCH] = creature->GetGUID();
					break;
				case NPC_SARA:
					m_saraGUID = creature->GetGUID();
					break;
				case NPC_BRAIN_OF_YOGG_SARON:
					m_yoggsaronBrainGUID = creature->GetGUID();
					break;
				case NPC_BRANN_BRONZBEARD_ALG:
                    m_brannBronzebeardAlgGUID = creature->GetGUID();
                    break;
				//! These creatures are summoned by something else than Algalon
                //! but need to be controlled/despawned by him - so they need to be
                //! registered in his summon list
                case NPC_ALGALON_VOID_ZONE_VISUAL_STALKER:
                case NPC_ALGALON_STALKER_ASTEROID_TARGET_01:
                case NPC_ALGALON_STALKER_ASTEROID_TARGET_02:
                case NPC_UNLEASHED_DARK_MATTER:
                    if (Creature* algalon = instance->GetCreature(m_uiAlgalonGUID))
                        algalon->AI()->JustSummoned(creature);
                    break;
			}
		}

		void OnCreatureRemove(Creature* creature)
        {
            switch (creature->GetEntry())
            {
                case NPC_BRANN_BRONZBEARD_ALG:
                    if (m_brannBronzebeardAlgGUID == creature->GetGUID())
                        m_brannBronzebeardAlgGUID = 0;
                    break;
            }
        }

		void OpenIfDone(uint32 encounter, GameObject *go, GOState state)
		{
			if (GetData(encounter) == DONE)
				go->SetGoState(state);
		}

		void ShowKeeperGossip(uint8 type, Creature *cr, uint64 guid = 0)
		{
			if (!cr)
			{
				cr = instance->GetCreature(guid);
				if (!cr)
					return;
			}

			bool on = (GetData(type) == DONE && !(GetData(TYPE_WATCHERS) & (1 << (type-TYPE_FREYA))));
			cr->SetVisible(on ? true : false);
		}

		void OnGameObjectCreate(GameObject* gameObject)
		{
			switch (gameObject->GetEntry())
			{
				// Flame Leviathan
				case GO_REPAIR_STATION_TRAP:
					{
						if(m_RepairSGUID[0])
							m_RepairSGUID[1] = gameObject->GetGUID();
						else
							m_RepairSGUID[0] = gameObject->GetGUID();
						break;
					}
				case GO_LIGHTNING_WALL1:
					m_lightningWalls[0] = gameObject->GetGUID();
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					break;
				case GO_LIGHTNING_WALL2:
					m_lightningWalls[1] = gameObject->GetGUID();
					break;
				case GO_MIMIRONS_TARGETTING_CRYSTAL:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[3][0] = gameObject->GetGUID();
					break;
				case GO_FREYAS_TARGETTING_CRYSTAL:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[0][0] = gameObject->GetGUID();
					break;
				case GO_HODIRS_TARGETTING_CRYSTAL:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[2][0] = gameObject->GetGUID();
					break;
				case GO_THORIMS_TARGETTING_CRYSTAL:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[1][0] = gameObject->GetGUID();
					break;
				case GO_MIMIRONS_GENERATOR:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[3][1] = gameObject->GetGUID();
					break;
				case GO_FREYAS_GENERATOR:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[0][1] = gameObject->GetGUID();
					break;
				case GO_HODIRS_GENERATOR:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[2][1] = gameObject->GetGUID();
					break;
				case GO_THORIMS_GENERATOR:
					OpenIfDone(TYPE_LEVIATHAN, gameObject, GO_STATE_ACTIVE);
					m_leviathanVisualTowers[1][1] = gameObject->GetGUID();
					break;
				case GO_LEVIATHAN_DOORS:
					if (GetData(TYPE_LEVIATHAN) >= DONE)
						gameObject->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
					m_leviathanDoorsGUID = gameObject->GetGUID();
					break;
				// XT-002, Kologarn, Assembly of Iron
				case GO_XT002_DOORS:
					m_xt002DoorsGUID = gameObject->GetGUID();
					break;
				case GO_XT002_DOOR:
					m_xt002Doors2GUID = gameObject->GetGUID();
					break;
				case GO_KOLOGARN_BRIDGE:
					OpenIfDone(TYPE_KOLOGARN, gameObject, GO_STATE_READY);
					break;
				case GO_ASSEMBLY_DOORS:
					m_assemblyDoorsGUID = gameObject->GetGUID();
					break;
				case GO_ARCHIVUM_DOORS:
					m_archivumDoorsGUID = gameObject->GetGUID();
					OpenIfDone(TYPE_ASSEMBLY, gameObject, GO_STATE_ACTIVE);
					break;
				// Thorim
				case GO_ARENA_LEVER_GATE:
					m_thorimGameobjectsGUID[DATA_THORIM_LEVER_GATE-DATA_THORIM_LEVER_GATE] = gameObject->GetGUID();
					break;
				case GO_ARENA_LEVER:
					m_thorimGameobjectsGUID[DATA_THORIM_LEVER-DATA_THORIM_LEVER_GATE] = gameObject->GetGUID();
					break;
				case GO_ARENA_FENCE:
					m_thorimGameobjectsGUID[DATA_THORIM_FENCE-DATA_THORIM_LEVER_GATE] = gameObject->GetGUID();
					break;
				case GO_FIRST_COLOSSUS_DOORS:
					m_thorimGameobjectsGUID[DATA_THORIM_FIRST_DOORS-DATA_THORIM_LEVER_GATE] = gameObject->GetGUID();
					break;
				case GO_SECOND_COLOSSUS_DOORS:
					m_thorimGameobjectsGUID[DATA_THORIM_SECOND_DOORS-DATA_THORIM_LEVER_GATE] = gameObject->GetGUID();
					break;
				// Yogg-Saron
				case GO_YOGG_SARON_DOORS:
					m_yoggsaronDoorsGUID = gameObject->GetGUID();
					break;
				case GO_KEEPERS_GATE:
					if (GetData(TYPE_MIMIRON) == DONE && GetData(TYPE_FREYA) == DONE && GetData(TYPE_HODIR) == DONE && GetData(TYPE_THORIM) == DONE)
					{
						instance->LoadGrid(1903.0f, 248.0f);
						gameObject->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
					}

					m_keepersgateGUID = gameObject->GetGUID();
					break;
				// Mimiron, Hodir, Vezax
				case GO_MIMIRON_ELEVATOR:
					gameObject->EnableCollision(false);
					break;
				case GO_MIMIRON_DOOR_1:
					m_MimironDoor[0] = gameObject->GetGUID();
					break;
				case GO_MIMIRON_DOOR_2:
					m_MimironDoor[1] = gameObject->GetGUID();
					break;
				case GO_MIMIRON_DOOR_3:
					m_MimironDoor[2] = gameObject->GetGUID();
					break;
				case GO_HODIR_FROZEN_DOOR:
				case GO_HODIR_DOOR:
					if (GetData(TYPE_HODIR) == DONE)
						if( gameObject->GetGoState() != GO_STATE_ACTIVE )
						{
							gameObject->SetLootState(GO_READY);
							gameObject->UseDoorOrButton(0, false);
						}
					break;
				case GO_VEZAX_DOOR:
					if( GetData(TYPE_VEZAX) == DONE )
						if( gameObject->GetGoState() != GO_STATE_ACTIVE )
						{
							gameObject->SetLootState(GO_READY);
							gameObject->UseDoorOrButton(0, false);
						}
					break;
				case GO_SNOW_MOUND:
					gameObject->EnableCollision(false);
					break;
				case GO_MIMIRON_TRAM:
					if (GetData(TYPE_MIMIRON) == DONE)
						m_mimironTramUsed = true;
                    m_mimironTramGUID = gameObject->GetGUID();
                    break;
				// Algalon the Observer
				case GO_CELESTIAL_PLANETARIUM_ACCESS_10:
                case GO_CELESTIAL_PLANETARIUM_ACCESS_25:
                    if (m_algalonTimer)
                        gameObject->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
                    break;
                case GO_DOODAD_UL_SIGILDOOR_01:
                    m_algalonSigilDoorGUID[0] = gameObject->GetGUID();
                    if (m_algalonTimer)
                        gameObject->SetGoState(GO_STATE_ACTIVE);
                    break;
                case GO_DOODAD_UL_SIGILDOOR_02:
                    m_algalonSigilDoorGUID[1] = gameObject->GetGUID();
                    if (m_algalonTimer)
                        gameObject->SetGoState(GO_STATE_ACTIVE);
                    break;
                case GO_DOODAD_UL_SIGILDOOR_03:
                    m_algalonSigilDoorGUID[2] = gameObject->GetGUID();
                    break;
                case GO_DOODAD_UL_UNIVERSEFLOOR_01:
                    m_algalonFloorGUID[0] = gameObject->GetGUID();
                    break;
                case GO_DOODAD_UL_UNIVERSEFLOOR_02:
                    m_algalonFloorGUID[1] = gameObject->GetGUID();
                    break;
                case GO_DOODAD_UL_UNIVERSEGLOBE01:
                    m_algalonUniverseGUID = gameObject->GetGUID();
                    break;
                case GO_DOODAD_UL_ULDUAR_TRAPDOOR_03:
                    m_algalonTrapdoorGUID = gameObject->GetGUID();
                    break;
				// Herbs
				case 191019: // Adder's Tongue
				case 190176: // Frost Lotus
				case 190171: // Lichbloom
				case 190170: // Talandra's Rose
				case 189973: // Goldclover
					if (GetData(TYPE_FREYA) == DONE)
						gameObject->SetRespawnTime(7*DAY);
					break;
			}
		}

		void SetData(uint32 type, uint32 data)
		{
			switch(type)
			{
				case TYPE_LEVIATHAN:
					m_auiEncounter[type] = data;
					if (data == DONE)
					{
						Map::PlayerList const& pList = instance->GetPlayers();
						for (Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
							if (Vehicle* veh = itr->GetSource()->GetVehicle())
								veh->Dismiss();
					}
					break;
				case TYPE_IGNIS:
				case TYPE_RAZORSCALE:
				case TYPE_XT002:
				case TYPE_AURIAYA:
				case TYPE_VEZAX:
				case TYPE_YOGGSARON:
				case TYPE_KOLOGARN:
					m_auiEncounter[type] = data;
					break;
				case TYPE_ASSEMBLY:
					if (GameObject* go = instance->GetGameObject(m_assemblyDoorsGUID))
						go->SetGoState(data == IN_PROGRESS ? GO_STATE_READY : GO_STATE_ACTIVE);
					if (GameObject* go = instance->GetGameObject(m_archivumDoorsGUID))
						go->SetGoState(data == DONE ? GO_STATE_ACTIVE : GO_STATE_READY);

					m_auiEncounter[type] = data;
					break;
				case TYPE_MIMIRON:
				case TYPE_HODIR:
				case TYPE_THORIM:
				case TYPE_FREYA:
					m_auiEncounter[type] = data;
					ShowKeeperGossip(type, NULL, m_keepersGossipGUID[type-TYPE_FREYA]);
					if (GetData(TYPE_MIMIRON) == DONE && GetData(TYPE_FREYA) == DONE && GetData(TYPE_HODIR) == DONE && GetData(TYPE_THORIM) == DONE)
					{
						if (GameObject* go = instance->GetGameObject(m_keepersgateGUID))
							go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
					}
					if (type == TYPE_MIMIRON && data == IN_PROGRESS) // after reaching him without tram and starting the fight
						m_mimironTramUsed = true;
					break;

				case TYPE_WATCHERS:
					m_auiEncounter[type] |= 1 << data;
					break;

				case EVENT_TOWER_OF_LIFE_DESTROYED:
				case EVENT_TOWER_OF_STORM_DESTROYED:
				case EVENT_TOWER_OF_FROST_DESTROYED:
				case EVENT_TOWER_OF_FLAMES_DESTROYED:
					{
						instance->LoadGrid(364.0f, -16.0f); //make sure leviathan is loaded
						m_leviathanTowers[type-EVENT_TOWER_OF_LIFE_DESTROYED] = data;
						GameObject* gobj = NULL;
						if (gobj = instance->GetGameObject(m_leviathanVisualTowers[type-EVENT_TOWER_OF_LIFE_DESTROYED][0]))
							gobj->SetGoState(GO_STATE_ACTIVE);
						if (gobj = instance->GetGameObject(m_leviathanVisualTowers[type-EVENT_TOWER_OF_LIFE_DESTROYED][1]))
							gobj->SetGoState(GO_STATE_ACTIVE);
						return;
					}

				case DATA_VEHICLE_SPAWN:
					SpawnLeviathanEncounterVehicles(data);
					return;
				case DATA_UNBROKEN_ACHIEVEMENT:
					m_unbrokenAchievement = data;
					SaveToDB();
					return;
				case DATA_DESPAWN_ALGALON:
					DoUpdateWorldState(WORLD_STATE_ALGALON_TIMER_ENABLED, 1);
					DoUpdateWorldState(WORLD_STATE_ALGALON_DESPAWN_TIMER, 60);
					m_algalonTimer = 60;
					_events.RescheduleEvent(EVENT_UPDATE_ALGALON_TIMER, 60000);
					SaveToDB();
					return;
				case DATA_ALGALON_SUMMON_STATE:
				case DATA_ALGALON_DEFEATED:
					DoUpdateWorldState(WORLD_STATE_ALGALON_TIMER_ENABLED, 0);
					m_algalonTimer = (type == DATA_ALGALON_DEFEATED ? TIMER_ALGALON_DEFEATED : TIMER_ALGALON_SUMMONED);
					_events.CancelEvent(EVENT_UPDATE_ALGALON_TIMER);
					SaveToDB();
					return;
				case TYPE_ALGALON:
					m_auiEncounter[type] = data;
					if (GameObject* go = instance->GetGameObject(GetData64(GO_DOODAD_UL_SIGILDOOR_03)))
					{
						go->SetGoState(data != IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY);
						go->EnableCollision(false);
					}
					if (GameObject* go = instance->GetGameObject(GetData64(GO_DOODAD_UL_UNIVERSEFLOOR_01)))
					{
						go->SetGoState(data != IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY);
						go->EnableCollision(false);
					}
					if (GameObject* go = instance->GetGameObject(GetData64(GO_DOODAD_UL_UNIVERSEFLOOR_02)))
					{
						go->SetGoState(data == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY);
						go->EnableCollision(false);
					}
					if (GameObject* go = instance->GetGameObject(GetData64(GO_DOODAD_UL_UNIVERSEGLOBE01)))
					{
						go->SetGoState(data == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY);
						go->EnableCollision(false);
					}
					if (GameObject* go = instance->GetGameObject(GetData64(GO_DOODAD_UL_ULDUAR_TRAPDOOR_03)))
					{
						go->SetGoState(data == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY);
						go->EnableCollision(false);
					}
					break;

				// Achievement
				case DATA_DWARFAGEDDON:
					DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_SPELL_TARGET, SPELL_DWARFAGEDDON);
					DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, SPELL_DWARFAGEDDON);
					return;
				case DATA_CALL_TRAM:
                    if (GameObject* MimironTram = instance->GetGameObject(m_mimironTramGUID))
						if (StaticTransport* t = MimironTram->ToStaticTransport())
						{
							if (data == 0 && t->GetGoState() == GO_STATE_ACTIVE && t->GetPathProgress() == t->GetPauseTime())
								MimironTram->SetGoState(GO_STATE_READY);
							if (data == 1 && t->GetGoState() == GO_STATE_READY && t->GetPathProgress() == 0)
								MimironTram->SetGoState(GO_STATE_ACTIVE);
						}
                    break;
			}

			// take care of herbs
			if (type == TYPE_FREYA && data == DONE)
			{
				std::list<GameObject*> goList;
				if (Creature* freya = instance->GetCreature(GetData64(TYPE_FREYA)))
				{
					freya->GetGameObjectListWithEntryInGrid(goList, 191019 /*Adder's Tongue*/, 333.0f);
					freya->GetGameObjectListWithEntryInGrid(goList, 190176 /*Frost Lotus*/, 333.0f);
					freya->GetGameObjectListWithEntryInGrid(goList, 190171 /*Lichbloom*/, 333.0f);
					freya->GetGameObjectListWithEntryInGrid(goList, 190170 /*Talandra's Rose*/, 333.0f);
					freya->GetGameObjectListWithEntryInGrid(goList, 189973 /*Goldclover*/, 333.0f);
					
					for (std::list<GameObject*>::const_iterator itr = goList.begin(); itr != goList.end(); ++itr)
						(*itr)->SetRespawnTime(7*DAY);
				}
			}

			if (data == DONE || type == TYPE_LEVIATHAN || type == TYPE_WATCHERS)
				SaveToDB();

			if (type > TYPE_LEVIATHAN && type < TYPE_WATCHERS && data == IN_PROGRESS)
			{
				Map::PlayerList const& pList = instance->GetPlayers();
				for (Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
					if (Vehicle* veh = itr->GetSource()->GetVehicle())
						veh->Dismiss();
			}
		}

		uint64 GetData64(uint32 data) const
		{
			switch(data)
			{
				// Bosses
				case TYPE_LEVIATHAN:
					return m_uiLeviathanGUID;
				case TYPE_IGNIS:
					return m_uiIgnisGUID;
				case TYPE_RAZORSCALE:
					return m_uiRazorscaleGUID;
				case TYPE_XT002:
					return m_uiXT002GUID;
				case TYPE_KOLOGARN:
					return m_uiKologarnGUID;
				case TYPE_AURIAYA:
					return m_uiAuriayaGUID;
				case TYPE_MIMIRON:
					return m_uiMimironGUID;
				case TYPE_HODIR:
					return m_uiHodirGUID;
				case TYPE_THORIM:
					return m_uiThorimGUID;
				case TYPE_FREYA:
					return m_uiFreyaGUID;
				case TYPE_VEZAX:
					return m_uiVezaxGUID;
				case TYPE_YOGGSARON:
					return m_uiYoggSaronGUID;
				case TYPE_ALGALON:
					return m_uiAlgalonGUID;
				case DATA_STEELBREAKER:
					return m_auiAssemblyGUIDs[0];
				case DATA_MOLGEIM:
					return m_auiAssemblyGUIDs[1];
				case DATA_BRUNDIR:
					return m_auiAssemblyGUIDs[2];

				// Flame Leviathan
				case DATA_REPAIR_STATION1:
					return m_RepairSGUID[0];
				case DATA_REPAIR_STATION2:
					return m_RepairSGUID[1];
				case DATA_LIGHTNING_WALL1:
					return m_lightningWalls[0];
				case DATA_LIGHTNING_WALL2:
					return m_lightningWalls[1];
				case GO_LEVIATHAN_DOORS:
					return m_leviathanDoorsGUID;

				// Razorscales Harpoon Fire State GUIDs
				case DATA_HARPOON_FIRE_STATE_1:
				case DATA_HARPOON_FIRE_STATE_2:
				case DATA_HARPOON_FIRE_STATE_3:
				case DATA_HARPOON_FIRE_STATE_4:
					return m_RazorscaleHarpoonFireStateGUID[data-200];

				// XT-002
				case GO_XT002_DOORS:
					return m_xt002DoorsGUID;
				case GO_XT002_DOOR:
					return m_xt002Doors2GUID;

				// Thorim
				case DATA_THORIM_LEVER_GATE:
				case DATA_THORIM_LEVER:
				case DATA_THORIM_FENCE:
				case DATA_THORIM_FIRST_DOORS:
				case DATA_THORIM_SECOND_DOORS:
					return m_thorimGameobjectsGUID[data-DATA_THORIM_LEVER_GATE];
					break;

				// Freya Elders
				case NPC_ELDER_IRONBRANCH:
				case NPC_ELDER_STONEBARK:
				case NPC_ELDER_BRIGHTLEAF:
					return m_FreyaElder[data-NPC_ELDER_IRONBRANCH];

				// Mimiron's first vehicle (spawned by default)
				case DATA_MIMIRON_LEVIATHAN_MKII:
					return m_MimironLeviathanMKIIguid;
				case DATA_MIMIRON_VX001:
					return m_MimironVX001guid;
				case DATA_MIMIRON_ACU:
					return m_MimironACUguid;
				case DATA_GO_MIMIRON_DOOR_1:
				case DATA_GO_MIMIRON_DOOR_2:
				case DATA_GO_MIMIRON_DOOR_3:
					return m_MimironDoor[data-311];

				// Yogg-Saron
				case GO_YOGG_SARON_DOORS:
					return m_yoggsaronDoorsGUID;
				case NPC_SARA:
					return m_saraGUID;
				case NPC_BRAIN_OF_YOGG_SARON:
					return m_yoggsaronBrainGUID;

				// Algalon the Observer
				case GO_DOODAD_UL_SIGILDOOR_01:
					return m_algalonSigilDoorGUID[0];
				case GO_DOODAD_UL_SIGILDOOR_02:
					return m_algalonSigilDoorGUID[1];
				case GO_DOODAD_UL_SIGILDOOR_03:
					return m_algalonSigilDoorGUID[2];
				case GO_DOODAD_UL_UNIVERSEFLOOR_01:
					return m_algalonFloorGUID[0];
				case GO_DOODAD_UL_UNIVERSEFLOOR_02:
					return m_algalonFloorGUID[1];
				case GO_DOODAD_UL_UNIVERSEGLOBE01:
					return m_algalonUniverseGUID;
				case GO_DOODAD_UL_ULDUAR_TRAPDOOR_03:
					return m_algalonTrapdoorGUID;
				case NPC_BRANN_BRONZBEARD_ALG:
					return m_brannBronzebeardAlgGUID;
			}

			return 0;
		}

		uint32 GetData(uint32 type) const
		{
			switch(type)
			{
				case TYPE_LEVIATHAN:
				case TYPE_IGNIS:
				case TYPE_RAZORSCALE:
				case TYPE_XT002:
				case TYPE_ASSEMBLY:
				case TYPE_KOLOGARN:
				case TYPE_AURIAYA:
				case TYPE_MIMIRON:
				case TYPE_HODIR:
				case TYPE_THORIM:
				case TYPE_FREYA:
				case TYPE_VEZAX:
				case TYPE_YOGGSARON:
				case TYPE_ALGALON:
				case TYPE_WATCHERS:
					return m_auiEncounter[type];

				case EVENT_TOWER_OF_LIFE_DESTROYED:
				case EVENT_TOWER_OF_STORM_DESTROYED:
				case EVENT_TOWER_OF_FROST_DESTROYED:
				case EVENT_TOWER_OF_FLAMES_DESTROYED:
					return m_leviathanTowers[type-EVENT_TOWER_OF_LIFE_DESTROYED];

				case DATA_UNBROKEN_ACHIEVEMENT:
					return m_unbrokenAchievement;

				case DATA_CALL_TRAM:
					return m_mimironTramUsed;
			}

			return 0;
		}

		void OnUnitDeath(Unit* unit)
		{
			// Feeds on Tears achievement
			if (unit->GetTypeId() == TYPEID_PLAYER)
			{
				if (GetData(TYPE_ALGALON) == IN_PROGRESS)
					if (Creature* algalon = instance->GetCreature(m_uiAlgalonGUID))
						algalon->AI()->DoAction(ACTION_FEEDS_ON_TEARS_FAILED);
			}
			else if (unit->GetTypeId() == TYPEID_UNIT && unit->GetAreaId() == 4656 /*Conservatory of Life*/)
			{
				if (time(NULL) > (m_conspeedatoryAttempt + DAY))
				{
					DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, 21597 /*CON-SPEED-ATORY_TIMED_CRITERIA*/);
					m_conspeedatoryAttempt = time(NULL);
					SaveToDB();
				}
			}

			// achievement Champion/Conqueror of Ulduar
			if (unit->GetTypeId() == TYPEID_PLAYER)
				for (uint8 i = 0; i <= 12; ++i)
				{
					bool go = false;
					if (i == TYPE_LEVIATHAN)
					{
						if (Creature* c = instance->GetCreature(m_uiLeviathanGUID))
							if (c->IsInCombat())
								go = true;
					}
					else
						go = (m_auiEncounter[i] == IN_PROGRESS);

					if (go && (C_of_Ulduar_MASK & (1<<i)) == 0)
					{
						C_of_Ulduar_MASK |= (1<<i);
						SaveToDB();
					}
				}
		}

		std::string GetSaveData()
		{
			OUT_SAVE_INST_DATA;

			std::ostringstream saveStream;
			saveStream << "U U " << m_auiEncounter[0] << ' ' << m_auiEncounter[1] << ' ' << m_auiEncounter[2] << ' ' << m_auiEncounter[3] << ' '
					   << m_auiEncounter[4] << ' ' << m_auiEncounter[5] << ' ' << m_auiEncounter[6] << ' ' << m_auiEncounter[7] << ' '
					   << m_auiEncounter[8] << ' ' << m_auiEncounter[9] << ' ' << m_auiEncounter[10] << ' ' << m_auiEncounter[11] << ' '
					   << m_auiEncounter[12] << ' ' << m_auiEncounter[13] << ' ' << m_auiEncounter[14] << ' ' << m_conspeedatoryAttempt << ' '
					   << m_unbrokenAchievement << ' ' << m_algalonTimer << ' ' << C_of_Ulduar_MASK;

			OUT_SAVE_INST_DATA_COMPLETE;
			return saveStream.str();
		}

		void Load(const char* strIn)
		{
			if (!strIn)
			{
				OUT_LOAD_INST_DATA_FAIL;
				return;
			}

			OUT_LOAD_INST_DATA(strIn);

			char dataHead1, dataHead2;

			std::istringstream loadStream(strIn);
			loadStream >> dataHead1 >> dataHead2;

			if (dataHead1 == 'U' && dataHead2 == 'U')
			{
				for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
				{
					loadStream >> m_auiEncounter[i];
	                
					if (m_auiEncounter[i] == IN_PROGRESS && i != TYPE_WATCHERS)
						m_auiEncounter[i] = NOT_STARTED;
				}

				// Achievements
				loadStream >> m_conspeedatoryAttempt;
				loadStream >> m_unbrokenAchievement;

				// Algalon
                loadStream >> m_algalonTimer;
				if (m_algalonTimer == TIMER_ALGALON_SUMMONED)
					m_algalonTimer = TIMER_ALGALON_TO_SUMMON;

                if (m_algalonTimer && m_algalonTimer <= 60 && GetData(TYPE_ALGALON) != DONE)
                {
                    DoUpdateWorldState(WORLD_STATE_ALGALON_TIMER_ENABLED, 1);
                    DoUpdateWorldState(WORLD_STATE_ALGALON_DESPAWN_TIMER, m_algalonTimer);
                }

				// achievement Conqueror/Champion of Ulduar
				loadStream >> C_of_Ulduar_MASK;
			}

			OUT_LOAD_INST_DATA_COMPLETE;
		}


        void Update(uint32 diff)
        {
            if (_events.Empty())
                return;

            _events.Update(diff);
            switch (_events.GetEvent())
            {
                case EVENT_UPDATE_ALGALON_TIMER:
					if (m_algalonTimer == TIMER_ALGALON_DEFEATED)
					{
						_events.PopEvent();
						return;
					}

                    SaveToDB();
                    DoUpdateWorldState(WORLD_STATE_ALGALON_DESPAWN_TIMER, --m_algalonTimer);
                    if (m_algalonTimer)
					{
                        _events.RepeatEvent(60000);
						return;
					}

					_events.PopEvent();
					SetData(DATA_ALGALON_DEFEATED, 1);
                    if (Creature* algalon = instance->GetCreature(m_uiAlgalonGUID))
                        algalon->AI()->DoAction(ACTION_DESPAWN_ALGALON);
            }
        }

		void SpawnLeviathanEncounterVehicles(uint8 mode);

		bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* source, Unit const* target = NULL, uint32 miscvalue1 = 0)
		{
			switch (criteria_id)
			{
				case 10042:
				case 10352:
					return (C_of_Ulduar_MASK & (1<<TYPE_LEVIATHAN)) == 0;
				case 10342:
				case 10355:
					return (C_of_Ulduar_MASK & (1<<TYPE_IGNIS)) == 0;
				case 10340:
				case 10353:
					return (C_of_Ulduar_MASK & (1<<TYPE_RAZORSCALE)) == 0;
				case 10341:
				case 10354:
					return (C_of_Ulduar_MASK & (1<<TYPE_XT002)) == 0;
				case 10598:
				case 10599:
					return (C_of_Ulduar_MASK & (1<<TYPE_ASSEMBLY)) == 0;
				case 10348:
				case 10357:
					return (C_of_Ulduar_MASK & (1<<TYPE_KOLOGARN)) == 0;
				case 10351:
				case 10363:
					return (C_of_Ulduar_MASK & (1<<TYPE_AURIAYA)) == 0;
				case 10439:
				case 10719:
					return (C_of_Ulduar_MASK & (1<<TYPE_HODIR)) == 0;
				case 10403:
				case 10404:
					return (C_of_Ulduar_MASK & (1<<TYPE_THORIM)) == 0;
				case 10582:
				case 10583:
					return (C_of_Ulduar_MASK & (1<<TYPE_FREYA)) == 0;
				case 10347:
				case 10361:
					return (C_of_Ulduar_MASK & (1<<TYPE_MIMIRON)) == 0;
				case 10349:
				case 10362:
					return (C_of_Ulduar_MASK & (1<<TYPE_VEZAX)) == 0;
				case 10350:
				case 10364:
					return (C_of_Ulduar_MASK & (1<<TYPE_YOGGSARON)) == 0;
			}
			return false;
		}
	};
};

const Position vehiclePositions[30] = 
{
	// Start Positions
	// Siege
	{-814.592f, -64.5436f, 429.927f, 5.96903f},
	{-784.371f, -33.3111f, 429.927f, 5.09636f},
	{-813.698f, -86.8924f, 430.158f, 6.0912f},
	{-739.3f, -21.51f, 429.927f, 4.86947f},
	{-756.948f, -27.9419f, 429.927f, 5.07891f},
	// Chopper
	{-717.556f, -111.2f, 430.157f, 0.0910799f},
	{-717.833f, -106.567f, 430.024f, 0.122173f},
	{-718.451f, -118.248f, 430.27f, 0.05236f},
	{-717.337f, -113.591f, 430.279f, 0.0910799f},
	{-717.076f, -116.456f, 430.361f, 0.0910799f},
	// Demolisher
	{-766.702f, -225.033f, 430.503f, 1.71042f},
	{-729.545f, -186.269f, 430.128f, 1.90241f},
	{-793.69f, -240.574f, 430.981f, 1.64061f},
	{-719.747f, -165.845f, 430.135f, 1.95477f},
	{-732.267f, -203.694f, 432.463f, 2.07694f},
	// Leviathan Positions
	// Siege
	{119.8f, 38.37f, 409.803f, 0.0f},
	{119.8f, 28.37f, 410.803f, 0.0f},
	{119.8f, 18.37f, 409.803f, 0.0f},
	{119.8f, 8.37f, 409.803f, 0.0f},
	{119.8f, -2.37f, 409.803f, 0.0f},
	// Chopper
	{119.8f, -17.37f, 409.803f, 0.0f},
	{119.8f, -27.37f, 409.803f, 0.0f},
	{119.8f, -37.37f, 409.803f, 0.0f},
	{119.8f, -47.37f, 409.803f, 0.0f},
	{119.8f, -57.37f, 409.803f, 0.0f},
	// Demolisher
	{119.8f, -72.37f, 409.803f, 0.0f},
	{119.8f, -82.37f, 409.803f, 0.0f},
	{119.8f, -92.37f, 409.803f, 0.0f},
	{119.8f, -102.37f, 409.803f, 0.0f},
	{119.8f, -112.37f, 409.803f, 0.0f},
};


void instance_ulduar::instance_ulduar_InstanceMapScript::SpawnLeviathanEncounterVehicles(uint8 mode)
{
	if (!_leviathanVehicles.empty())
	{
		for (std::list<uint64>::iterator itr = _leviathanVehicles.begin(); itr != _leviathanVehicles.end(); ++itr)
			if (Creature* cr = instance->GetCreature(*itr))
				if (Vehicle* veh = cr->GetVehicleKit())
					veh->Dismiss();

		_leviathanVehicles.clear();
	}

	if (mode < VEHICLE_POS_NONE)
	{
		TempSummon* veh = NULL;
		for (uint8 i = 0; i < (instance->Is25ManRaid() ? 5 : 2); ++i)
		{
			if (veh = instance->SummonCreature(NPC_SALVAGED_SIEGE_ENGINE, vehiclePositions[15*mode+i]))
				_leviathanVehicles.push_back(veh->GetGUID());
			if (veh = instance->SummonCreature(NPC_VEHICLE_CHOPPER, vehiclePositions[15*mode+i+5]))
				_leviathanVehicles.push_back(veh->GetGUID());
			if (veh = instance->SummonCreature(NPC_SALVAGED_DEMOLISHER, vehiclePositions[15*mode+i+10]))
				_leviathanVehicles.push_back(veh->GetGUID());
		}
	}
}

void AddSC_instance_ulduar()
{
    new instance_ulduar();
}
